package mbp.agents;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import mbp.base.MessageType;
import mbp.base.Settings;
import mbp.core.Resource;
import mbp.core.ResourceUtility;
import mbp.core.ResourceValidation;
import mbp.exceptions.ParseExpressionErrorException;
import mbp.logger.LoggerProxy;
import mbp.message.Message;
import mbp.message.MessageXml;
import mbp.message.resource;
import mbp.message.utility;
import mbp.message.validation;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.domain.JADEAgentManagement.JADEManagementOntology;
import jade.domain.JADEAgentManagement.SniffOn;
import jade.lang.acl.ACLMessage;

/**
 * Agent bazowy dla systemu MBP. Implementuje Metody diagnostyczne zarzadzania podstawowymi obiektami.
 * Obiekty sa przechowywane w strukturach HashMap
 * @author wojtek
 *
 */
public abstract class AMbpAgentMap extends Agent
{
	//pamiec na obiekty Resource
	public Map<String, Resource> resourceList;
	
	//pamiec na obiekty ResourceUtility
	public Map<String, ResourceUtility> utilityList;
	
	//pamiec na obiekty ResourceValidation
	public Map<String, ResourceValidation> validationList;
	
	protected Logger logger = Logger.getLogger(this.getClass());
	
	@Override
	protected void setup()
	{
		resourceList = Collections.synchronizedMap(new HashMap<String, Resource>());
		utilityList = Collections.synchronizedMap(new HashMap<String, ResourceUtility>());
		validationList = Collections.synchronizedMap(new HashMap<String, ResourceValidation>());
		
		ProcessMassage processMessage = new ProcessMassage();
		addBehaviour(processMessage);
		
		//System.out.println("This is base setup");
	}
	
	public void addAgentToSniffer(String agentName)
	{
		String snifferName = Settings.snifferName;
		SniffOn agact = new SniffOn();
		agact.addSniffedAgents(new AID(agentName, AID.ISLOCALNAME));
		agact.setSniffer(new AID(snifferName, AID.ISLOCALNAME));
		
		ACLMessage msg=new ACLMessage(ACLMessage.REQUEST);
		msg.setLanguage(new SLCodec().getName());
		msg.setOntology(JADEManagementOntology.getInstance().getName());
		getContentManager().registerLanguage(new SLCodec());
		getContentManager().registerOntology(JADEManagementOntology.getInstance());

		Action act=new Action(new AID(snifferName, AID.ISLOCALNAME),agact);
		try {
			getContentManager().fillContent(msg,act);
		} catch (CodecException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (OntologyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		msg.addReceiver(new AID(snifferName, AID.ISLOCALNAME));
		SenderLogger.send(this, msg);
	}
	
	private void processDiagMessage(mbp.message.Message iMsg, String senderName)
	{
		MessageType messageType = iMsg.getMessageType();
		switch(messageType)
		{
		case RESOURCELGET:
			onResourceLGet(iMsg, senderName);
			break;
		case RESOURCEGET:
			onResourceGet(iMsg, senderName);
			break;
		case RESOURCE:
			onResource(iMsg, senderName);
			break;
		case RESOURCEDEL:
			onResourceDel(iMsg, senderName);
			break;
		case UTILITYLGET:
			onUtilityLGet(iMsg, senderName);
			break;
		case UTILITY:
			onUtility(iMsg, senderName);
			break;
		case UTILITYGET:
			onUtilityGet(iMsg, senderName);
			break;
		case UTILITYDEL:
			onUtilityDel(iMsg, senderName);
			break;
		case VALIDATIONLGET:
			onValidationLGet(iMsg, senderName);
			break;
		case VALIDATION:
			onValidation(iMsg, senderName);
			break;
		case VALIDATIONGET:
			onValidationGet(iMsg, senderName);
			break;
		case VALIDATIONDEL:
			onValidationDel(iMsg, senderName);
			break;
		default:
			processMessage(iMsg, senderName);
			return;	
		}
		processMessage(iMsg, senderName);
	}
	
	/**
	 * Metoda przeznaczona do nadpisania. Obsluga pozostalych typow wiadomosci zaleznie od rodzaju agenta.
	 * @param iMsg
	 * @param senderName
	 */
	protected abstract void processMessage(mbp.message.Message iMsg, String senderName);
	
	/**
	 * Odpowiedz na wiadomosc typu RESOURCELGET
	 * @param iMsg
	 * @param senderName
	 */
	protected void onResourceLGet(mbp.message.Message iMsg, String senderName)
	{
		Iterator<String> it = resourceList.keySet().iterator();
		List<resource> rciList = new ArrayList<resource>();
		while(it.hasNext())
		{
			String keyUuid = it.next();
			resource rc = new resource();
			rc.type = resourceList.get(keyUuid).getType().getResourceTypeName();
			rc.owner = resourceList.get(keyUuid).getOwner();
			rc.resourceid = resourceList.get(keyUuid).getUUID();			
			rciList.add(rc);
		}
		
		MessageXml mxml = new MessageXml();
		for(int i = 0; i<rciList.size(); i++)
		{
			mxml.resources.add(rciList.get(i));
		}
		Message message = new Message(MessageType.RESOURCEL, getLocalName(), senderName);
		message.setContentXml(mxml);
		ACLMessage msgReply = message.GetACLMessage();
		SenderLogger.send(this, msgReply);
	}
	
	protected void onResourceGet(mbp.message.Message iMsg, String senderName)
	{	
		String uuid = iMsg.getContentXml().resources.get(0).resourceid;
		Resource rc = null;
		
		if(resourceList.containsKey(uuid))
		{
			rc = resourceList.get(uuid);
			Message message = new Message(MessageType.RESOURCE, getLocalName(), senderName);
			message.setResource(rc);
			SenderLogger.send(this, message.GetACLMessage());
		}
		else
		{
			Message message = new Message(MessageType.ERROR, getLocalName(), senderName);
			SenderLogger.send(this, message.GetACLMessage());
		}
	}
	
	protected void onResource(mbp.message.Message iMsg, String senderName)
	{
		try {
			Resource resourceTmp = iMsg.getResource();
			resourceList.put(resourceTmp.getUUID(), resourceTmp);
		} catch (Exception e) {
			logger.error("Exception", e);
		}
	}
	
	protected void onResourceDel(mbp.message.Message iMsg, String senderName)
	{
		String uuid = iMsg.getContentXml().resources.get(0).resourceid;
		if(resourceList.remove(uuid)==null)
		{
			Message message = new Message(MessageType.ERROR, getLocalName(), senderName);
			SenderLogger.send(this, message.GetACLMessage());
		}
	}
	
	//-----------------------------------------------------------------------------------------------------------------
	
	protected void onUtilityLGet(mbp.message.Message iMsg, String senderName)
	{
		Iterator<String> it = utilityList.keySet().iterator();
		Message message = new Message(MessageType.UTILITYL, getLocalName(), senderName);
		while(it.hasNext())
		{
			utility ut = new utility();
			String key = it.next();
			ut.type = utilityList.get(key).getType().getResourceTypeName();
			ut.expression = utilityList.get(key).getExrpession();
			message.getContentXml().utilities.add(ut);
		}
		SenderLogger.send(this, message.GetACLMessage());
	}
	
	protected void onUtilityGet(mbp.message.Message iMsg, String senderName)
	{
		String type = iMsg.getContentXml().utilities.get(0).type;
		ResourceUtility ru = null;
		if(utilityList.containsKey(type))
		{
			ru = utilityList.get(type);
			Message message = new Message(MessageType.UTILITY, getLocalName(), senderName);
			message.setResourceUtility(ru);
			SenderLogger.send(this, message.GetACLMessage());
		}
		else
		{
			Message message = new Message(MessageType.ERROR, getLocalName(), senderName);
			SenderLogger.send(this, message.GetACLMessage());
		}
	}
	
	protected void onUtility(mbp.message.Message iMsg, String senderName)
	{
		ResourceUtility ru = iMsg.getResourceUtility().get(0);
		utilityList.put(ru.getType().getResourceTypeName(), ru);
	}
	
	protected void onUtilityDel(mbp.message.Message iMsg, String senderName)
	{
		String key = iMsg.getContentXml().utilities.get(0).type;
		if(utilityList.remove(key)==null)
		{
			Message message = new Message(MessageType.ERROR, getLocalName(), senderName);
			SenderLogger.send(this, message.GetACLMessage());
		}
	}
	
	//-----------------------------------------------------------------------------------------------------------------
	
	protected void onValidationLGet(mbp.message.Message iMsg, String senderName)
	{
		Iterator<String> it = validationList.keySet().iterator();
		Message message = new Message(MessageType.VALIDATIONL, getLocalName(), senderName);
		while(it.hasNext())
		{
			validation vt = new validation();
			String key = it.next();
			vt.type = validationList.get(key).getType().getResourceTypeName();
			vt.condition = validationList.get(key).getCondition();
			message.getContentXml().validations.add(vt);
		}
		SenderLogger.send(this, message.GetACLMessage());
	}
	
	protected void onValidation(mbp.message.Message iMsg, String senderName)
	{
		ResourceValidation rv = iMsg.getResourceValidation().get(0);
		validationList.put(rv.getType().getResourceTypeName(), rv);
	}
	
	protected void onValidationGet(mbp.message.Message iMsg, String senderName)
	{
		String type = iMsg.getContentXml().validations.get(0).type;
		ResourceValidation rv = null;
		if(validationList.containsKey(type))
		{
			rv = validationList.get(type);
			Message message = new Message(MessageType.VALIDATION, getLocalName(), senderName);
			message.setResourceValidation(rv);
			SenderLogger.send(this, message.GetACLMessage());
		}
		else
		{
			Message message = new Message(MessageType.ERROR, getLocalName(), senderName);
			SenderLogger.send(this, message.GetACLMessage());
		}
	}
	
	protected void onValidationDel(mbp.message.Message iMsg, String senderName)
	{
		String key = iMsg.getContentXml().validations.get(0).type;
		if(validationList.remove(key)==null)
		{
			Message message = new Message(MessageType.ERROR, getLocalName(), senderName);
			SenderLogger.send(this, message.GetACLMessage());
		}
	}
	
	
	
	public class ProcessMassage extends CyclicBehaviour
	{

		@Override
		public void action() 
		{
			ACLMessage msg = myAgent.receive();
			
			if(msg != null)
			{
				LoggerProxy.logReceiving(msg, myAgent);
				logger.info("Recieved message from " + msg.getSender().getName());
				logger.debug(msg.toString());
				mbp.message.Message iMsg = new mbp.message.Message(msg);
				if(iMsg.isValid())
				{
					logger.info("Message is valid - type: " + iMsg.getMessageType());
					processDiagMessage(iMsg, msg.getSender().getLocalName());
				}
				else
				{
					if(iMsg.isJedeMgm())
					{
						logger.info("JADE-Agent-Management " + msg.getSender().getName() + " : " +
								jade.lang.acl.ACLMessage.getPerformative(msg.getPerformative()));
					}
					else
					{
						logger.error("Message in not valid!");
					}
				}
			}
			else
			{
				block();
			}
		}		
	}

}
